package com.ruoyi.vote.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.config.XytpVoteConfig;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.UserType;
import com.ruoyi.common.exception.vote.VoteException;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.mall.domain.OrderInfo;
import com.ruoyi.mall.enums.OrderInfoEnum;
import com.ruoyi.vote.admin.domain.VoteActivity;
import com.ruoyi.vote.admin.domain.VotePrizeReceive;
import com.ruoyi.vote.admin.domain.dto.VoteActivityRuleDto;
import com.ruoyi.vote.admin.mapper.VotePrizeReceiveMapper;
import com.ruoyi.vote.admin.service.IVoteLikeService;
import com.ruoyi.vote.common.constants.PayConstants;
import com.ruoyi.vote.common.strategy.ActivityContext;
import com.ruoyi.vote.common.strategy.UserLickCheck;
import com.ruoyi.vote.common.utils.KeyUtils;
import com.ruoyi.vote.common.utils.VoteCheckUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBatch;
import org.springframework.stereotype.Service;
import com.ruoyi.vote.admin.domain.bo.VoteLikeBo;
import com.ruoyi.vote.admin.domain.vo.VoteLikeVo;
import com.ruoyi.vote.admin.domain.VoteLike;
import com.ruoyi.vote.admin.mapper.VoteLikeMapper;

import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Collection;

/**
 * 活动 - 投票信息Service业务层处理
 *
 * @author 筑巢
 * @date 2023-07-17
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class VoteLikeServiceImpl extends ServiceImpl<VoteLikeMapper, VoteLike>  implements IVoteLikeService {

    /**
     * 系统基础配置
     */
    private final XytpVoteConfig voteConfig;
    private final VoteLikeMapper baseMapper;

    /**
     * 查询活动 - 投票信息
     */
    @Override
    public VoteLikeVo queryById(Long id){
        return baseMapper.selectVoById(id);
    }

    /**
     * 查询活动 - 投票信息列表
     */
    @Override
    public TableDataInfo<VoteLikeVo> queryPageList(VoteLikeBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<VoteLike> lqw = buildQueryWrapper(bo);
        Page<VoteLikeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 预约投票 ( 不会增加票数 )
     *
     * @param bo
     * @return
     */
    @Override
    public Boolean preLike(VoteLikeBo bo) {
        VoteLike voteLike = BeanUtil.toBean(bo, VoteLike.class);
        voteLike.setIsPay(PayConstants.ERROR);
        voteLike.setStatus(OrderInfoEnum.STATUS_0.getValue());
        boolean flg = baseMapper.insert(voteLike) > 0;
        if(flg){
            bo.setId(voteLike.getId());
        }
        return flg;
    }

    private LambdaQueryWrapper<VoteLike> buildQueryWrapper(VoteLikeBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<VoteLike> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getAId() != null, VoteLike::getAId, bo.getAId());
        lqw.eq(bo.getMId() != null, VoteLike::getMId, bo.getMId());
        lqw.eq(bo.getTicketTime() != null, VoteLike::getTicketTime, bo.getTicketTime());
        return lqw;
    }

    /**
     * 投票逻辑
     */
    @Override
    public Boolean likeUserVoting(String userId, String eventId, String number) {
        log.info("----- start 投票开始 - 活动ID:[{}] , 选手:[{}] , 投票人: [{}] -----",eventId,number,userId);

        String eventKey = KeyUtils.formatInfoEventKey(eventId);
        String acsKey = KeyUtils.formatAcsKey(eventId, number);

        // 场景校验
        ActivityContext.setActivityStatus(new UserLickCheck());
        VoteActivity voteActivity = ActivityContext.checkActivity(eventId);

        // 选手检查票数
        VoteCheckUtils.memberCheck(acsKey);

        String likeEventKey = KeyUtils.formatLikeEventKey(userId, DateUtils.dateTime(), eventId);
        String eventTotalKey = KeyUtils.formatEventTotalKey(eventId);

        Integer likeCount = RedisUtils.getCacheObject(likeEventKey);
        if (likeCount == null) {
            likeCount = voteConfig.getVote().getBasis();
            RedisUtils.setCacheObject(likeEventKey, likeCount, Duration.ofHours(24));
        }
        Integer activityNumber = voteConfig.getVote().getLimit();
        VoteActivityRuleDto voteRuleDto = JSON.parseObject(voteActivity.getVoteRule(), VoteActivityRuleDto.class);
        if(ObjectUtil.isNotNull(voteRuleDto) && ObjectUtil.isNotEmpty(voteRuleDto.getLimit())){
            activityNumber = voteRuleDto.getLimit();
        }
        if (likeCount >= activityNumber && UserType.XCX_MEMBER.equals(LoginHelper.getUserType())) {
            log.info("----- 免费票数上限 - key: [{}] ,投票上限:[{}] ,投票前票数:[{}] -----", likeEventKey,activityNumber,likeCount);
            throw new VoteException("vote.activity.user.upperLimit",userId);
        }
        log.info("----- 今日投票数 - key: [{}] ,投票上限:[{}] ,投票前票数:[{}] -----", likeEventKey,activityNumber,likeCount);
        // 创建批处理对象
        RBatch batch = RedisUtils.getClient().createBatch();
        // 用户对活动投票+1
        RedisUtils.incrAtomicValue(likeEventKey);
        // 选手的票+1
        RedisUtils.incrAtomicValue(acsKey);
        // 总票数+1
        RedisUtils.incrAtomicValue(eventTotalKey);
        // 提交事务
        batch.execute();
        log.info("----- end 投票结束 - 选手票数: [{}] ,投票人结束票数:[{}] ,投票时间: {} -----", RedisUtils.getAtomicValue(acsKey),likeCount+1,DateUtils.getTime());
        return true;
    }

    /**
     * 添加指定票数
     */
    @Override
    public Boolean likeUserVoting(String userId, String eventId, String number, Integer num){
        String likeEventKey = KeyUtils.formatLikeEventKey(userId, DateUtils.dateTime(), eventId);
        String acsKey = KeyUtils.formatAcsKey(eventId, number);
        String eventTotalKey = KeyUtils.formatEventTotalKey(eventId);

        // 场景校验
        ActivityContext.setActivityStatus(new UserLickCheck());
        VoteActivity activity = ActivityContext.checkActivity(eventId);
        // 选手检查票数
        VoteCheckUtils.memberCheck(acsKey);

        Integer likeCount = RedisUtils.getCacheObject(likeEventKey);
        if (likeCount == null) {
            likeCount = voteConfig.getVote().getBasis();
            RedisUtils.setCacheObject(likeEventKey, likeCount, Duration.ofHours(24));
        }

        // 用户对活动投票+n
        long likeNum = RedisUtils.getAtomicValue(likeEventKey);
        likeNum += num;
        RedisUtils.setAtomicValue(likeEventKey,likeNum);

        // 选手的票+n
        long acsKeyNum = RedisUtils.getAtomicValue(acsKey);
        acsKeyNum += num;
        RedisUtils.setAtomicValue(acsKey,acsKeyNum);

        // 总票数+n
        long eventNum = RedisUtils.getAtomicValue(eventTotalKey);
        eventNum += num;
        RedisUtils.setAtomicValue(eventTotalKey,eventNum);

        return true;
    }

}
